package mc0101.mccache.util;

import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


@Slf4j
public final class ConvertUtils {

    /**
     * 把map的list转换成实体类的list
     * @param classType 类类型
     * @param params 转换参数
     * @param <T> 类型
     * @return 转换结果
     */
    public static <T> List<T> convertBeanList(Class<T> classType, List<Map<String, Object>> params){
        if (params == null || params.size() == 0){
            return new ArrayList<>();
        }

        long begin = System.currentTimeMillis();
        List<T> resultBeanList = new ArrayList<>(params.size());
        for (Map<String, Object> param : params){
            T obj = convertBean(classType, param);
            resultBeanList.add(obj);
        }
        log.debug("类:"+classType.getName()+"转换:"+params.size()+",消耗时间为："+(System.currentTimeMillis()-begin));
        return resultBeanList;
    }
    /**
     * 把map转换成实体类
     * @param classType 转换的类型
     * @param param map参数
     * @param <T> 类型
     * @return 转换结果
     */
    public static <T> T convertBean(Class<T> classType, Map<String, Object> param) {
        T obj = null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(classType);
            obj = classType.newInstance(); // 创建 JavaBean 对象

            // 给 JavaBean 对象的属性赋值
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                if (param.containsKey(propertyName)) {
                    // 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
                    Object value = param.get(propertyName);
                    if ("".equals(value)) {
                        value = null;
                    }
                    Object[] args = new Object[1];
                    try {
                        args[0] = convertValueByPropertyType(value, descriptor.getPropertyType());
                        descriptor.getWriteMethod().invoke(obj, args);
                    } catch (Exception e) {
                        log.error("数据转换异常", e);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            log.error("实例化 JavaBean 失败", e);
        } catch (IntrospectionException e) {
            log.error("分析类属性失败", e);
        } catch (IllegalArgumentException e) {
            log.error("映射错误", e);
        } catch (InstantiationException e) {
            log.error("实例化 JavaBean 失败", e);
        }
        return obj;
    }

    private static Object convertValueByPropertyType(Object object, Class clazz) throws ParseException {
        if (clazz.equals(Timestamp.class)){
            if (object instanceof Timestamp){
                return object;
            }
            if (object instanceof Long){
                return new Timestamp(Long.parseLong(String.valueOf(object)));
            }
            if (object instanceof String){
                if (((String) object).contains("T")){
                    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
                    return new Timestamp(sdf1.parse((String) object).getTime());
                }
                if (((String) object).contains("-") && ((String) object).contains(":") && ((String) object).contains(".")){
                    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
                    return new Timestamp(sdf1.parse((String) object).getTime());
                }
                if (((String) object).contains("-") && ((String) object).contains(":")){
                    SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    return new Timestamp(sdf1.parse((String) object).getTime());
                }
                return new Timestamp(Long.parseLong(String.valueOf(object)));
            }
            return new Timestamp(Long.parseLong(String.valueOf(object)));
        } if (clazz.equals(Long.class)){
            return Long.parseLong(String.valueOf(object));
        } if (clazz.equals(Integer.class)){
            return Integer.parseInt(String.valueOf(object));
        } if (clazz.equals(Date.class)){
            return convertDate(object);
        }
        return object;
    }

    private static Object convertDate(Object object) {
        String value = String.valueOf(object);
        if (value != null && value.length() == 13){
            return new Date(Long.parseLong(String.valueOf(object)));
        }
        return object;
    }

    /**
     *
     * @param bean
     * @return
     */
    public static Map<String, Object> convertMap(Object bean) {
        Class<? extends Object> clazz = bean.getClass();
        Map<String, Object> returnMap = new HashMap<String, Object>();
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < propertyDescriptors.length; i++) {
                PropertyDescriptor descriptor = propertyDescriptors[i];
                String propertyName = descriptor.getName();
                if (!propertyName.equals("class")) {
                    Method readMethod = descriptor.getReadMethod();
                    Object result = null;
                    result = readMethod.invoke(bean, new Object[0]);
                    if (null != propertyName) {
                        propertyName = propertyName.toString();
                    }
                    if (null != result) {
                        result = result.toString();
                    }
                    returnMap.put(propertyName, result);
                }
            }
        } catch (IntrospectionException e) {
            log.error("分析类属性失败", e);
        } catch (IllegalAccessException e) {
            log.error("实例化 JavaBean 失败", e);
        } catch (IllegalArgumentException e) {
            log.error("映射错误", e);
        } catch (InvocationTargetException e) {
            log.error("调用属性的 setter 方法失败", e);
        }
        return returnMap;
    }

    /**
     * 将下划线大写或小写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。</br>
     * 例如：HELLO_WORLD->HelloWorld
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String camelName(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，全部小写
            return name.toLowerCase();
        }
        // 用下划线将原始字符串分割
        String camels[] = name.split("_");
        for (String camel :  camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 处理真正的驼峰片段
            if (result.length() == 0) {
                // 第一个驼峰片段，全部字母都小写
                result.append(camel.toLowerCase());
            } else {
                // 其他的驼峰片段，首字母大写
                result.append(camel.substring(0, 1).toUpperCase());
                result.append(camel.substring(1).toLowerCase());
            }
        }
        return result.toString();
    }

    /**
     * 把map的list中所有属性的下划线命名key转换成驼峰
     * @param entityList map的list
     * @return 转换后的
     */
    public static List<Map<String, Object>> camelMapList(List<Map<String, Object>> entityList) {
        List<Map<String, Object>> entityCamelList = new ArrayList<Map<String, Object>>();
        for (Map<String, Object> item : entityList){
            Map<String, Object> resultMap = new HashMap<>();
            item.forEach((key, value) -> resultMap.put(ConvertUtils.camelName(key), value));
            entityCamelList.add(resultMap);
        }
        return entityCamelList;
    }

}